﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using r2mp_DataStructures;
using r2mp_MTA.Symbs;
using r2mp_MTA;

namespace r2mp_GUI
{
    public partial class MTAWorker : r2mp_DataStructures.DiagramWorker
    {
        private int error = 0;
        private Brush[] colors = { Brushes.Red, Brushes.Orange, Brushes.DarkOrchid, Brushes.LightGreen, 
                                       Brushes.Green, Brushes.Teal, Brushes.LightBlue, Brushes.Blue, 
                                       Brushes.Violet, Brushes.Black };
        private int index = 0;
        private int text = 0;
        private MTA mta = null;
        private DateTime first = DateTime.MinValue;
        private DateTime last = DateTime.MinValue;
        private int dateCount = 0;
        private int drawn = 0;
        private List<MTAWrapper> milestones = new List<MTAWrapper>();
        private Font font;
        private Pen pen;
        private Panel panel;
        private List<String> errors = new List<string>();
        private bool errorShown = false;

        public MTAWorker(MTA mta, Panel panel)
        {
            this.panel = panel;
            this.mta = mta;
            GlobalConfig.Periode = mta.Periode;
            setDates();
            countDate();
            prepareMilestones();
            if (mta.Periode == MTA.Month)
            {
                first = first.AddDays(-(first.Day - 1));
            }
            else
            {
                //Set the date to a monday because timeshifts are calculated from mondays.
                switch (first.DayOfWeek)
                {
                    case DayOfWeek.Tuesday:
                        first = first.AddDays(-1);
                        break;
                    case DayOfWeek.Wednesday:
                        first = first.AddDays(-2);
                        break;
                    case DayOfWeek.Thursday:
                        first = first.AddDays(-3);
                        break;
                    case DayOfWeek.Friday:
                        first = first.AddDays(-4);
                        break;
                    case DayOfWeek.Saturday:
                        first = first.AddDays(-5);
                        break;
                    case DayOfWeek.Sunday:
                        first = first.AddDays(-6);
                        break;
                }
            }
            secondCheck();
            Scale();
        }

        /// <summary>
        /// Expands the grid if a timeshift would be greater than the original grid
        /// </summary>
        private void secondCheck()
        {
            int temp;
            List<MTAWrapper> remove = new List<MTAWrapper>();
            foreach (MTAWrapper now in milestones)
            {
                temp = now.Id;
                foreach (int timeshift in now.Item.Timeshifts)
                {
                    temp += timeshift;
                    if (temp < 1)
                    {
                        error = 1;
                        errors.Add(now.Item.Name);
                        remove.Add(now);
                    }
                    else if (temp > dateCount)
                    {
                        dateCount = temp;
                    }
                }



            }
            if (error != 0)
            {
                foreach (MTAWrapper now in remove)
                {
                    milestones.Remove(now);
                }
            }
        }

        /// <summary>
        /// Wraps the milestones in the local class MTWWrapper.
        /// </summary>
        private void prepareMilestones()
        {
            DateTime temp;
            int count;
            r2mp_MTA.Symbols symb = null;
            for (int i = 0; i < mta.Milestones.Count; i++)
            {
                temp = first;
                count = 0;
                //This determines the starting position of the milestone.
                while (temp.Date <= mta.Milestones[i].PlannedCompletionDate.Date)
                {
                    if (GlobalConfig.Periode == MTA.Month)
                    {
                        temp = temp.AddMonths(1);
                    }
                    else
                    {
                        temp = temp.AddDays(7);
                    }
                    count++;
                }


                if (i == 0)
                {
                    symb = new Rect();
                }
                else if (i == mta.Milestones.Count - 1)
                {
                    symb = new Elips();
                }
                else
                {
                    switch (index % 5)
                    {
                        case 0:
                            symb = new Star();
                            break;
                        case 1:
                            symb = new Tri();
                            break;
                        case 2:
                            symb = new Tri2();
                            break;
                        case 3:
                            symb = new Tri3();
                            break;
                        case 4:
                            symb = new Tri4();
                            break;
                    }
                }
                milestones.Add(new MTAWrapper(count, mta.Milestones[i], colors[index],symb));
                index++;
                //Modulo 10 because there are only 10 colors!
                index = index % 10;
            }
        }

        /// <summary>
        /// Scales the whole MTA.
        /// </summary>
        public void Scale()
        {
            int length = 50;
            //Calculates the length of the dates which is important for the position of the grid.
            text = (int)(TextRenderer.MeasureText(first.ToShortDateString(), font).Width + 5 * GlobalConfig.Scale);
            GlobalConfig.Width = length * GlobalConfig.Scale;
            font = new Font(Form.DefaultFont.FontFamily, GlobalConfig.Width / 4);
            if (drawn == 1)
            {
                if (error != 0 && !errorShown)
                {
                    for (int i = 0; i < errors.Count; i++)
                    {
                        MessageBox.Show("Der Meilensteint " + errors[i] + "enthält ungültige Daten!\n"
                            + "(Ein Fertigstellungsdatum in der Vergangenheit)", "Achtung!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    error = 0;
                    errorShown = true;
                }
                drawn = 2;
            }
        }

        /// <summary>
        /// Counts the dates. This is important later for drawing the timeshifts of each milestone.
        /// </summary>
        private void countDate()
        {
            DateTime temp = first;
            while ((temp.Date <= last.Date))
            {
                if (GlobalConfig.Periode == MTA.Month)
                {
                    dateCount++;
                    temp = temp.AddMonths(1);
                }
                else
                {
                    dateCount++;
                    temp = temp.AddDays(7);
                }
            }
        }

        /// <summary>
        /// Sets the earliest and the latest date. Needed for the size of the grid.
        /// </summary>
        private void setDates()
        {
            foreach (Milestone now in mta.Milestones)
            {
                if (first == DateTime.MinValue)
                {
                    first = now.PlannedCompletionDate;
                    last = now.PlannedCompletionDate;
                }
                if (now.PlannedCompletionDate < first)
                {
                    first = now.PlannedCompletionDate;
                }
                else if (now.PlannedCompletionDate > last)
                {
                    last = now.PlannedCompletionDate;
                }
            }
        }


        public void draw(Graphics g)
        {

            Scale();
            int nextVal;
            panel.Height = (int)(text + GlobalConfig.Width * dateCount);
            panel.Width = (int)(text * 2 + GlobalConfig.Width * dateCount + 30 * GlobalConfig.Scale);
            if (text + font.Height * milestones.Count > panel.Height)
            {
                panel.Height = text + font.Height * milestones.Count;
            }

            drawGrid(g);

            drawLegend(g);
            

            List<PointF> points;
            int ind = 0;
            foreach (MTAWrapper now in milestones)
            {
                points = new List<PointF>();

                //Calculates the startingposition of a milestone.
                int count = dateCount - now.Id;
                float y = count * GlobalConfig.Width + text;
                float x = GlobalConfig.Width / 2 + text;
                y += GlobalConfig.Width / 2;

                PointF temp = new PointF(x, y);
                points.Add(temp);

                nextVal = now.Id;

                int dateValue = now.Id;
                float tempx = x;
                float tempy = y;
                //Calculates the position of the timeshifts for this milestone.
                for (int i = 0; i < nextVal && i < now.Item.Timeshifts.Count; i++)
                {
                    //Checks if the current timeshift would be beneath the digonal line.
                    if (now.Item.Timeshifts[i] != 0 && i + 1 >= nextVal + now.Item.Timeshifts[i])
                    {
                        error = 1;
                        if (!errors.Contains(now.Item.Name))
                        {
                            errors.Add(now.Item.Name);
                        }
                        points = null;
                        break;
                    }
                    else if (!(now.Item.Timeshifts[i] == 0 && i + 1 == nextVal))
                    {
                        tempx += GlobalConfig.Width;
                        tempy -= GlobalConfig.Width * now.Item.Timeshifts[i];
                        temp = new PointF(tempx, tempy);
                        dateValue += now.Item.Timeshifts[i];
                        points.Add(temp);

                        nextVal += now.Item.Timeshifts[i];
                    }
                }

                //Fills the pointsarray so that the milestone will continue until it
                //reaches the diagonal line.
                while (points != null && points.Count < dateValue)
                {
                    tempx = points[points.Count - 1].X + GlobalConfig.Width;
                    tempy = points[points.Count - 1].Y;
                    temp = new PointF(tempx, tempy);
                    points.Add(temp);
                }

                //Draws the line for the current milestone.
                if (points != null)
                {
                    pen = new Pen(now.Color, GlobalConfig.Width / 20);
                    
                    if (points.Count > 1)
                    {
                        g.DrawLines(pen, points.ToArray());
                    }

                    //Draws the symbols for the current milestone.
                    float rectWidth = GlobalConfig.Width / 3;
                    foreach (PointF rec in points)
                    {
                        now.Symb.drawIt(g, rec, rectWidth, now.Color);
                    }

                    //Draws the legend entry of the current milestone.
                    temp = new PointF(panel.Width - (30 * GlobalConfig.Scale) - text, text + ind * font.Height);
                    g.DrawLine(pen, temp.X, temp.Y + font.Height / 2, temp.X + (25 * GlobalConfig.Scale), temp.Y + font.Height / 2);
                    g.DrawString(now.Item.Name, font, Brushes.Black, temp.X + (30 * GlobalConfig.Scale), temp.Y);
                    ind++;
                }
            }
            if (drawn == 0)
            {
                drawn = 1;
            }
        }

        /// <summary>
        /// Draws the date legend left and above the grid.
        /// </summary>
        /// <param name="g">Graphics object of the panel.</param>
        private void drawLegend(Graphics g)
        {
            StringFormat sf = new StringFormat();
            sf.FormatFlags = StringFormatFlags.DirectionVertical;
            for (int i = 0; i < dateCount; i++)
            {
                if (mta.Periode == MTA.Month)
                {
                    float starty = i * GlobalConfig.Width + text + TextRenderer.MeasureText(first.AddMonths(i).ToShortDateString(), font).Height / 2;
                    float startx = i * GlobalConfig.Width + text + TextRenderer.MeasureText(first.AddMonths(i).ToShortDateString(), font).Height / 2;
                    g.DrawString(first.AddMonths(i).ToShortDateString(), font, Brushes.Black, startx, 0, sf);
                    g.DrawString(first.AddMonths(i).ToShortDateString(), font, Brushes.Black, 0, starty);
                }
                else
                {
                    float starty = i * GlobalConfig.Width + text + TextRenderer.MeasureText(first.AddDays(7 * i).ToShortDateString(), font).Height / 2;
                    float startx = i * GlobalConfig.Width + text + TextRenderer.MeasureText(first.AddDays(7 * i).ToShortDateString(), font).Height / 2;
                    g.DrawString(first.AddDays(i * 7).ToShortDateString(), font, Brushes.Black, startx, 0, sf);
                    g.DrawString(first.AddDays(i * 7).ToShortDateString(), font, Brushes.Black, 0, starty);

                }
            }
        }


        /// <summary>
        /// Draws the grid for the MTA.
        /// </summary>
        /// <param name="g">Graphics object of the panel.</param>
        private void drawGrid(Graphics g)
        {
            PointF start = new PointF(0 + text, 0 + text);
            PointF begin;
            PointF end;

            for (int i = dateCount; i > 0; i--)
            {
                begin = new PointF(start.X + (dateCount - i) * GlobalConfig.Width, start.Y);
                end = new PointF(start.X + (dateCount - i) * GlobalConfig.Width, start.Y + (i * GlobalConfig.Width));
                g.DrawLine(Pens.Black, begin, end);
                begin = new PointF(start.X, start.Y + ((dateCount - i) * GlobalConfig.Width));
                end = new PointF(start.X + i * GlobalConfig.Width, start.Y + ((dateCount - i) * GlobalConfig.Width));
                g.DrawLine(Pens.Black, begin, end);
            }
            begin = new PointF(start.X + dateCount * GlobalConfig.Width, start.Y);
            end = new PointF(start.X, start.Y + dateCount * GlobalConfig.Width);
            g.DrawLine(Pens.Black, begin, end);
        }

        /// <summary>
        /// Wrapperclass to combine milestone, id, color and symbol.
        /// </summary>
        private class MTAWrapper
        {
            private int id;

            public int Id
            {
                get { return id; }
                set { id = value; }
            }
            private Milestone item;

            public Milestone Item
            {
                get { return item; }
                set { item = value; }
            }
            private Brush col;

            public Brush Color
            {
                get { return col; }
                set { col = value; }
            }

            public MTAWrapper()
            {

            }

            public MTAWrapper(int id, Milestone item, Brush col)
            {
                this.id = id;
                this.item = item;
                this.col = col;
            }

            public MTAWrapper(int id, Milestone item, Brush col, r2mp_MTA.Symbols symb)
            {
                this.id = id;
                this.item = item;
                this.col = col;
                this.symb = symb;
            }

            private r2mp_MTA.Symbols symb;

            public r2mp_MTA.Symbols Symb
            {
                get { return symb; }
                set { symb = value; }
            }
        }



        public void zoomIn()
        {
            GlobalConfig.Scale += 0.05f;
            Scale();
        }

        public void zoomOut()
        {
            if (GlobalConfig.Scale - 0.05f > 0.2f)
            {
                GlobalConfig.Scale -= 0.05f;
                Scale();
            }
        }

        public Panel DrawingPanel
        {
            set { panel = value; }
        }
    }
}